;-------------------------------------------------------------------------------
format ELF64 executable 3
entry start

GLX_RENDER_TYPE = 0x8011
GLX_RGBA_BIT = 0x00000001
GLX_DRAWABLE_TYPE = 0x8010
GLX_WINDOW_BIT = 0x00000001
GLX_DOUBLEBUFFER = 5
GLX_RED_SIZE = 8
GLX_GREEN_SIZE = 9
GLX_BLUE_SIZE = 10
GLX_DEPTH_SIZE = 12
GLX_STENCIL_SIZE = 13

GL_VERTEX_SHADER = 0x8B31
GL_FRAGMENT_SHADER = 0x8B30

WIN_X = 10
WIN_Y = 10
WIN_W = 1024
WIN_H = 768

DEBUG = 1

macro emit [param] { param }
;-------------------------------------------------------------------------------
include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym
;-------------------------------------------------------------------------------
segment readable executable

start:
    mov rbp,rsp
    sub rsp,2048
    ; define local variables
    emit\
        <libgl equ rbp-8>,<disp equ rbp-16>,<root_win equ rbp-24>,<count equ rbp-28>,\
        <temp equ rbp-40>,<vis_info equ rbp-48>,<win_attribs equ rbp-328>,<win equ rbp-336>,<ctx equ rbp-344>,\
        <label XPending qword at rbp-352>,<label XNextEvent qword at rbp-360>,\
        <label glXSwapBuffers qword at rbp-368>,<event equ rbp-512>,<label glXGetProcAddress qword at rbp-520>,\
        <label glCreateShader qword at rbp-528>,<label glShaderSource qword at rbp-536>,\
        <label glCompileShader qword at rbp-544>,<label glCreateProgram qword at rbp-552>,\
        <label glLinkProgram qword at rbp-560>,<label glUseProgram qword at rbp-568>,\
        <label glAttachShader qword at rbp-576>,<label glGetUniformLocation qword at rbp-584>,\
        <label glUniform1f qword at rbp-592>,<label glUniform2i qword at rbp-600>,<label glRecti qword at rbp-608>,\
        <sh equ rbp-612>,<prog equ rbp-616>,<label clock qword at rbp-624>,<time_loc equ rbp-628>,\
        <label printf qword at rbp-636>,<label glGetProgramInfoLog qword at rbp-644>,<log equ rbp-2048>
    ; load libGL.so.1
    emit <mov rdi,g_str_libgl>,<xor esi,esi>,<inc esi>,<call [dlopen]>,<mov [libgl],rax>
    ; open display
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XOpenDisplay>,<call [dlsym]>
    emit <xor edi,edi>,<call rax>,<mov [disp],rax>
    ; get root window
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XRootWindow>,<call [dlsym]>
    emit <mov rdi,[disp]>,<xor esi,esi>,<call rax>,<mov [root_win],rax>
    ; get visual info
    emit <mov rdi,[libgl]>,<mov rsi,g_str_glXChooseFBConfig>,<call [dlsym]>
    emit <mov rdi,[disp]>,<xor esi,esi>,<mov rdx,g_fb_attribs>,<lea rcx,[count]>,<call rax>,<mov [temp],rax>
    emit <mov rdi,[libgl]>,<mov rsi,g_str_glXGetVisualFromFBConfig>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,[temp]>,<mov rsi,[rsi]>,<call rax>,<mov [vis_info],rax>
    ; set window attributes
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XCreateColormap>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,[root_win]>,<mov rdx,[vis_info]>,<mov rdx,[rdx]>,<xor ecx,ecx>,<call rax>
    emit <mov [win_attribs+96],rax>,<mov qword [win_attribs+72],1>,<mov qword [win_attribs+8],0>,<mov qword [win_attribs+24],0>
    ; create window
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XCreateWindow>,<call [dlsym]>
    emit\
        <lea rdi,[win_attribs]>,<push rdi>,<push (1 shl 1) or (1 shl 3) or (1 shl 13) or (1 shl 11)>,\
        <mov rdi,[vis_info]>,<push qword [rdi+0]>,<push 1>,<push qword [rdi+20]>,<push 0>,<mov r9d,WIN_H>,<mov r8d,WIN_W>,\
        <xor ecx,ecx>,<xor edx,edx>,<mov rsi,[root_win]>,<mov rdi,[disp]>,<call rax>,<add rsp,48>,<mov [win],rax>
    ; remove window border
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XInternAtom>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,g_str_motif_wm_hints>,<mov edx,1>,<call rax>,<mov [temp],rax>
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XChangeProperty>,<call [dlsym]>
    emit\
        <mov rdi,[disp]>,<mov rsi,[win]>,<mov rdx,[temp]>,<mov rcx,rdx>,<mov r8d,32>,<xor r9d,r9d>,\
        <push 10>,<mov r10,g_motif_wm_hints>,<push r10>,<call rax>,<add rsp,16>
    ; map window
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XMapWindow>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,[win]>,<call rax>
    ; move window
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XMoveWindow>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,[win]>,<mov edx,WIN_X>,<mov ecx,WIN_Y>,<call rax>
    ; create context
    emit <mov rdi,[libgl]>,<mov rsi,g_str_glXCreateContext>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,[vis_info]>,<xor edx,edx>,<xor ecx,ecx>,<inc ecx>,<call rax>,<mov [ctx],rax>
    ; make context current
    emit <mov rdi,[libgl]>,<mov rsi,g_str_glXMakeCurrent>,<call [dlsym]>
    emit <mov rdi,[disp]>,<mov rsi,[win]>,<mov rdx,[ctx]>,<call rax>
    ; load needed X and GLX functions
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XPending>,<call [dlsym]>,<mov [XPending],rax>
    emit <mov rdi,[libgl]>,<mov rsi,g_str_XNextEvent>,<call [dlsym]>,<mov [XNextEvent],rax>
    emit <mov rdi,[libgl]>,<mov rsi,g_str_glXSwapBuffers>,<call [dlsym]>,<mov [glXSwapBuffers],rax>
    emit <mov rdi,[libgl]>,<mov rsi,g_str_glXGetProcAddress>,<call [dlsym]>,<mov [glXGetProcAddress],rax>
    ; load needed GL commands
    emit <mov rdi,g_str_glCreateShader>,<call [glXGetProcAddress]>,<mov [glCreateShader],rax>
    emit <mov rdi,g_str_glShaderSource>,<call [glXGetProcAddress]>,<mov [glShaderSource],rax>
    emit <mov rdi,g_str_glCompileShader>,<call [glXGetProcAddress]>,<mov [glCompileShader],rax>
    emit <mov rdi,g_str_glCreateProgram>,<call [glXGetProcAddress]>,<mov [glCreateProgram],rax>
    emit <mov rdi,g_str_glLinkProgram>,<call [glXGetProcAddress]>,<mov [glLinkProgram],rax>
    emit <mov rdi,g_str_glUseProgram>,<call [glXGetProcAddress]>,<mov [glUseProgram],rax>
    emit <mov rdi,g_str_glAttachShader>,<call [glXGetProcAddress]>,<mov [glAttachShader],rax>
    emit <mov rdi,g_str_glGetUniformLocation>,<call [glXGetProcAddress]>,<mov [glGetUniformLocation],rax>
    emit <mov rdi,g_str_glUniform1f>,<call [glXGetProcAddress]>,<mov [glUniform1f],rax>
    emit <mov rdi,g_str_glUniform2i>,<call [glXGetProcAddress]>,<mov [glUniform2i],rax>
    emit <mov rdi,g_str_glRecti>,<call [glXGetProcAddress]>,<mov [glRecti],rax>
    ; load needed libc functions
    emit <mov rdi,[libgl]>,<mov rsi,g_str_clock>,<call [dlsym]>,<mov [clock],rax>
    ; create GLSL program
    emit <call [glCreateProgram]>,<mov [prog],eax>
    ; create and attach vertex shader
    emit <mov edi,GL_VERTEX_SHADER>,<call [glCreateShader]>,<mov [sh],eax>
    emit <mov edi,[prog]>,<mov esi,[sh]>,<call [glAttachShader]>
    emit <mov edi,[sh]>,<mov esi,1>,<mov rdx,g_src_vs_ptr>,<xor ecx,ecx>,<call [glShaderSource]>
    emit <mov edi,[sh]>,<call [glCompileShader]>
    ; create and attach fragment shader
    emit <mov edi,GL_FRAGMENT_SHADER>,<call [glCreateShader]>,<mov [sh],eax>
    emit <mov edi,[prog]>,<mov esi,[sh]>,<call [glAttachShader]>
    emit <mov edi,[sh]>,<mov esi,1>,<mov rdx,g_src_fs_ptr>,<xor ecx,ecx>,<call [glShaderSource]>
    emit <mov edi,[sh]>,<call [glCompileShader]>
    ; link and use program
    emit <mov edi,[prog]>,<call [glLinkProgram]>
    emit <mov edi,[prog]>,<call [glUseProgram]>
if DEBUG = 1
    ; print GLSL info log
    emit <mov rdi,g_str_glGetProgramInfoLog>,<call [glXGetProcAddress]>,<mov [glGetProgramInfoLog],rax>
    emit <mov rdi,[libgl]>,<mov rsi,g_str_printf>,<call [dlsym]>,<mov [printf],rax>
    emit <mov edi,[prog]>,<mov esi,1024>,<xor edx,edx>,<lea rcx,[log]>,<call [glGetProgramInfoLog]>
    emit <mov rdi,g_str_log>,<lea rsi,[log]>,<call [printf]>
end if
    ; get <time> uniform location
    emit <mov edi,[prog]>,<mov rsi,g_str_time>,<call [glGetUniformLocation]>,<mov [time_loc],eax>
    ; set <res> uniform
    emit <mov edi,[prog]>,<mov rsi,g_str_res>,<call [glGetUniformLocation]>
    emit <mov edi,eax>,<mov esi,WIN_W>,<mov edx,WIN_H>,<call [glUniform2i]>
    ; enter main loop
      .main_loop:
    ; process window events
    emit <mov rdi,[disp]>,<call [XPending]>
    test eax,eax
    jz .render
    emit <mov rdi,[disp]>,<lea rsi,[event]>,<call [XNextEvent]>
    cmp dword [event+0],2 ; key press
    je .main_loop_end
    jmp .main_loop
      .render:
    ; get time
    emit <xor edi,edi>,<call [clock]>
    ; set <time> uniform
    emit <cvtsi2ss xmm0,eax>,<divss xmm0,[g_ticks_per_sec]>,<mov edi,[time_loc]>,<call [glUniform1f]>
    ; draw
    emit <mov edi,-1>,<mov esi,-1>,<mov edx,1>,<mov ecx,1>,<call [glRecti]>
    ; swap
    emit <mov rdi,[disp]>,<mov rsi,[win]>,<call [glXSwapBuffers]>
    jmp .main_loop
      .main_loop_end:
    ; terminate process
    emit <mov eax,60>,<xor edi,edi>,syscall
;-------------------------------------------------------------------------------
segment readable writeable

g_ticks_per_sec dd 1000000.0
g_fb_attribs dd GLX_RENDER_TYPE,GLX_RGBA_BIT,GLX_DRAWABLE_TYPE,GLX_WINDOW_BIT,GLX_DOUBLEBUFFER,1,\
    GLX_RED_SIZE,8,GLX_GREEN_SIZE,8,GLX_BLUE_SIZE,8,GLX_DEPTH_SIZE,24,GLX_STENCIL_SIZE,8,0

g_src_vs:
db  '#version 400 compatibility',10
db  'void main(){'
db      'gl_Position=gl_Vertex;'
db  '}',0

g_src_fs:
db  '#version 400 compatibility',10
db  'uniform float time;'
db  'uniform ivec2 res;'
db  'vec4 mod289(vec4 x){'
db      'return x-floor(x*(1./289.))*289.;'
db  '}'
db  'vec4 permute(vec4 x){'
db      'return mod289(((x*34.)+1.)*x);'
db  '}'
db  'vec4 taylorInvSqrt(vec4 r){'
db      'return 1.79284291400159-0.85373472095314*r;'
db  '}'
db  'vec2 fade(vec2 t){'
db      'return t*t*t*(t*(t*6.-15.)+10.);'
db  '}'
db  'float pnoise(vec2 P,vec2 rep){'
db      'vec4 Pi=floor(P.xyxy)+vec4(0.,0.,1.,1.);'
db      'vec4 Pf=fract(P.xyxy)-vec4(0.,0.,1.,1.);'
db      'Pi=mod(Pi,rep.xyxy);'
db      'Pi=mod289(Pi);'
db      'vec4 ix=Pi.xzxz;'
db      'vec4 iy=Pi.yyww;'
db      'vec4 fx=Pf.xzxz;'
db      'vec4 fy=Pf.yyww;'
db      'vec4 i=permute(permute(ix)+iy);'
db      'vec4 gx=fract(i*(1./41.))*2.-1.;'
db      'vec4 gy=abs(gx)-.5;'
db      'vec4 tx=floor(gx+.5);'
db      'gx=gx-tx;'
db      'vec2 g00=vec2(gx.x,gy.x);'
db      'vec2 g10=vec2(gx.y,gy.y);'
db      'vec2 g01=vec2(gx.z,gy.z);'
db      'vec2 g11=vec2(gx.w,gy.w);'
db      'vec4 norm=taylorInvSqrt(vec4(dot(g00,g00),dot(g01,g01),dot(g10,g10),dot(g11,g11)));'
db      'g00*=norm.x;'
db      'g01*=norm.y;'
db      'g10*=norm.z;'
db      'g11*=norm.w;'
db      'float n00=dot(g00,vec2(fx.x,fy.x));'
db      'float n10=dot(g10,vec2(fx.y,fy.y));'
db      'float n01=dot(g01,vec2(fx.z,fy.z));'
db      'float n11=dot(g11,vec2(fx.w,fy.w));'
db      'vec2 fade_xy=fade(Pf.xy);'
db      'vec2 n_x=mix(vec2(n00,n01),vec2(n10,n11),fade_xy.x);'
db      'float n_xy=mix(n_x.x,n_x.y,fade_xy.y);'
db      'return 2.3*n_xy;'
db  '}'
db  'vec3 eye(float x,float y){'
db      'y-=.05;'
db      'x-=.1;'
db      'float rx=2.*(x-.5)*1.333;'
db      'float ry=2.*(y-.5);'
db      'float a=atan(ry,rx);'
db      'float e=rx*rx+ry*ry;'
db      'float r=sqrt(e);'
db      'vec3 fc=vec3(1.,1.,1.);'
db      'float ven=pnoise(vec2(28.*x,28.*y),vec2(256.,256.));'
db      'ven=smoothstep(-.2,.0,ven)-smoothstep(.0,.2,ven);'
db      'ven+=x+x*x*x*x*x*x*x*7.;'
db      'fc+=.04-vec3(.0,.07,.07)*ven;'
db      'vec3 dc=vec3(.3,.7,.4+e);'
db      'float no=.8+.2*pnoise(vec2(4.*r,32.*a/3.141592654),vec2(32.,32.));'
db      'dc*=no;'
db      'dc*=smoothstep(.025,.035,e);'
db      'vec3 col=mix(dc,fc,smoothstep(.35,.36,e));'
db      'col*=1.-.35*(smoothstep(.31,.35,e)-smoothstep(.35,.39,e));'
db      'float r1=sqrt(r*r*r);'
db      'float re=pnoise(vec2(.075*time+4.*r1*cos(a+.05*time),4.*r1*sin(a+.05*time)),vec2(256.,256.));'
db      're=.8*smoothstep(.1,.5,re);'
db      'col+=re*(1.-vec3(col));'
db      'return col;'
db  '}'
db  'vec3 skin(float x,float y){'
db      'float rx=2.*(x-.5)*1.333;'
db      'float ry=2.*(x-.5);'
db      'vec3 carne=vec3(.75,.69,.6);'
db      'float cel=.95+.05*pnoise(vec2(64.*x,64.*y),vec2(256.,256.));'
db      'carne*=cel;'
db      'carne.x+=.03*rx;'
db      'carne.y+=.03*ry;'
db      'carne *= y*.1+.9;'
db      'float bri=pnoise(vec2(128.*x,128.*y),vec2(256.,256.));'
db      'bri=.2+.8*smoothstep(bri,.0,.3);'
db      'carne+=bri*.08*y;'
db      'float san=.5*pnoise(vec2(16.*x,16.*y),vec2(256.,256.));'
db      'san+=.25*pnoise(vec2(32.*x,32.*y),vec2(256.,256.));'
db      'carne.y*=1.-.1*san;'
db      'float osc=.5*pnoise(vec2(12.*x,12.*y),vec2(256.,256.));'
db      'osc+=.25*pnoise(vec2(24.*x,24.*y),vec2(256.,256.));'
db      'osc+=.125*pnoise(vec2(48.*x,48.*y),vec2(256.,256.));'
db      'carne*=.9+.1*osc;'
db      'carne.x+=.08*x;'
db      'carne.y+=.01;'
db      'float pecas=pnoise(vec2(32.*x,32.*y),vec2(256.,256.));'
db      'pecas=smoothstep(pecas,.48,.6);'
db      'carne*=1.-.15*pecas;'
db      'return vec3(.0);'
db  '}'
db  'void main(){'
db      'vec2 p=gl_FragCoord.xy/res;'
db      'float rx=2.*(p.x-.5)*1.333;'
db      'float ry=2.*(p.y-.5);'
db      'float h=3.*sqrt(p.x*p.x*p.x)*(1.0-p.x);'
db      'float e=abs(ry)-h;'
db      'float f=smoothstep(.0,.025,e);'
db      'gl_FragColor=vec4(mix(eye(p.x,p.y),skin(p.x,p.y),f),1.);'
db '}',0

align 8
g_src_vs_ptr dq g_src_vs
g_src_fs_ptr dq g_src_fs
g_motif_wm_hints dq 2,0,0,0,0

g_str_glCreateShader db 'glCreateShader',0
g_str_glShaderSource db 'glShaderSource',0
g_str_glCompileShader db 'glCompileShader',0
g_str_glCreateProgram db 'glCreateProgram',0
g_str_glLinkProgram db 'glLinkProgram',0
g_str_glUseProgram db 'glUseProgram',0
g_str_glAttachShader db 'glAttachShader',0
g_str_glGetUniformLocation db 'glGetUniformLocation',0
g_str_glUniform1f db 'glUniform1f',0
g_str_glUniform2i db 'glUniform2i',0
g_str_glRecti db 'glRecti',0
g_str_libgl db 'libGL.so.1',0
g_str_XOpenDisplay db 'XOpenDisplay',0
g_str_XRootWindow db 'XRootWindow',0
g_str_glXChooseFBConfig db 'glXChooseFBConfig',0
g_str_glXGetVisualFromFBConfig db 'glXGetVisualFromFBConfig',0
g_str_XCreateColormap db 'XCreateColormap',0
g_str_XCreateWindow db 'XCreateWindow',0
g_str_glXCreateContext db 'glXCreateContext',0
g_str_glXMakeCurrent db 'glXMakeCurrent',0
g_str_XPending db 'XPending',0
g_str_XNextEvent db 'XNextEvent',0
g_str_glXSwapBuffers db 'glXSwapBuffers',0
g_str_glXGetProcAddress db 'glXGetProcAddress',0
g_str_XMapWindow db 'XMapWindow',0
g_str_clock db 'clock',0
g_str_time db 'time',0
g_str_res db 'res',0
g_str_motif_wm_hints db '_MOTIF_WM_HINTS',0
g_str_XInternAtom db 'XInternAtom',0
g_str_XChangeProperty db 'XChangeProperty',0
g_str_XMoveWindow db 'XMoveWindow',0
if DEBUG = 1
g_str_printf db 'printf',0
g_str_glGetProgramInfoLog db 'glGetProgramInfoLog',0
g_str_log db 'GLSL info log:',10,'%s',10,0
end if
;-------------------------------------------------------------------------------
